﻿///<copyright>
 ///Topbot (c) http://ya.topbloger.ru, 2008
 ///</copyright>

using System.Linq;
using System.Net.Http;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Web;

namespace BlogsAPI
{
    #region using

    using Interfaces;
    using System.Net;
    using Sites;
    using System.Reflection;
    using System.IO;
    using System.Text;
    using System;
    using System.Diagnostics;
    using RssToolkit.Rss;
    using System.Collections.Generic;
    using Abstract;
    using System.Text.RegularExpressions;
    using BlogsYandex;

    #endregion

    /// <summary>
    /// Все корневые функции для вызова чего-то одного
    /// </summary>
    public static class Common
    {
        /// <summary>
        /// Определение наличия схемы вначале ссылки
        /// </summary>
        private static readonly Regex rHttp = new Regex(@"^https?://(?:www\.)?",
            RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Singleline);

        #region Tools

        /// <summary>
        /// Добавляем http://
        /// </summary>
        /// <param name="sLink"></param>
        /// <returns></returns>
        public static String FixHttp(String sLink)
        {
            return rHttp.Match(sLink).Success
                ? sLink
                : "http://" + sLink;
        }

        /// <summary>
        /// Удаляем http://
        /// </summary>
        /// <param name="sLink"></param>
        /// <returns></returns>
        public static String DelHttp(String sLink)
        {
            return rHttp.Match(sLink).Success ? rHttp.Replace(sLink, "") : sLink;
        }

        /// <summary>
        /// Процедура создания
        /// </summary>
        /// <param name="link">запрашиваемая ссылка</param>
        /// <remarks>Эта функция должна вызываться только из конкретного экземпляра класса</remarks>
        public static HttpWebRequest MakeRequest(String link)
        {
            var ret = (HttpWebRequest) WebRequest.Create(link);
            ret.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1";
            ret.Accept =
                "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
            ret.Headers.Add("Accept-Language", "en-us;q=0.8,en;q=0.5,*;q=0.3");
            ret.Headers.Add("Accept-Charset", "utf-8;q=0.7,*;q=0.7");
            if (link.Contains("livejournal.com"))
            {
                ret.KeepAlive = false;//иначе глючит
            }
            return ret;
        }

        /// <summary>
        /// Процедура создания запроса с автоматической обработкой Gzip, Deflate
        /// </summary>
        /// <param name="link">запрашиваемая ссылка</param>
        /// <param name="allowAutoRedirect"></param>
        /// <param name="cookies"></param>
        /// <param name="methodPostContent">Говорит об использовании Post и задает контент для отправки</param>
        /// <remarks>Эта функция должна вызываться только из конкретного экземпляра класса</remarks>
        /// <remarks>TODO:in the future </remarks>
        [Obsolete("это имеет смысл для совместимости с .Net 4.0")]
        private static HttpResponseMessage MakeRequest2(String link,
            bool allowAutoRedirect = true, CookieCollection cookies = null, HttpContent methodPostContent = null)
        {
            var container = new CookieContainer();
            if (cookies != null) container.Add(new Uri(link), cookies);

            using (var handler = new HttpClientHandler
            {
                AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip,
                AllowAutoRedirect = allowAutoRedirect,
                CookieContainer = container,
                UseCookies = true
            })
            {
                var client = new System.Net.Http.HttpClient(handler) {Timeout = TimeSpan.FromMinutes(2)};
                client.DefaultRequestHeaders.Add("User-Agent",
                    "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:8.0.1) Gecko/20100101 Firefox/8.0.1");
                client.DefaultRequestHeaders.Add("Accept-Language", "en-us;q=0.8,en;q=0.5,*;q=0.3");
                client.DefaultRequestHeaders.Add("Accept-Charset", "utf-8;q=0.7,*;q=0.7");
                client.DefaultRequestHeaders.Referrer = new Uri(link);
                var responseTask = (methodPostContent == null
                    ? client.GetAsync(link, HttpCompletionOption.ResponseContentRead,
                        new CancellationToken())
                    : client.PostAsync(link, methodPostContent, new CancellationToken()));
                responseTask.ContinueWith(t =>
                    client.Dispose(), TaskContinuationOptions.ExecuteSynchronously);
                return responseTask.Result;
            }
        }

        private static readonly Regex rusLang = new Regex(@"[а-я](?:а|е|ё|и|о|у|ы|э|ю|я)",
            RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);

        /// <summary>
        /// Проверка наличия русской буквы
        /// </summary>
        /// <param name="sText"></param>
        /// <returns></returns>
        public static Boolean IsRussian(string sText)
        {
            if (String.IsNullOrEmpty(sText)) return false;
            return rusLang.Match(sText).Success;
        }

        /// <summary>
        /// Обработка запросов
        /// </summary>
        /// <param name="rss"></param>
        /// <returns></returns>
        public static Boolean IsRussian(RssDocument rss, double pers = 0.7)
        {
            if (pers >= 1.0) pers = 1.0;
            if (pers < 0.0) pers = 0.0;

            bool bRet = false;
            if (rss != null && rss.Channel != null)
            {
                int rusposts = 0;
                foreach (RssItem item in rss.Channel.Items)
                {
                    if (Common.IsRussian(item.Title + item.Description))
                    {
                        rusposts++;
                    }
                }
                //убедимся, что число русских постов больше 70%
                if (1.0*rusposts > rss.Channel.Items.Count*pers)
                {
                    bRet = true; //признали автора русским!
                }
            }
            return bRet;
        }

        /// <summary>
        /// Получение времени из Юниксового формата
        /// </summary>
        /// <param name="timestamp"></param>
        /// <returns></returns>
        public static DateTime ConvertFromUnixTimestamp(double timestamp)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return origin.AddSeconds(timestamp);
        }

        /// <summary>
        /// Получение времени Юниксового формата
        /// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public static double ConvertToUnixTimestamp(DateTime date)
        {
            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            TimeSpan diff = date - origin;
            return Math.Floor(diff.TotalSeconds);
        }

        #endregion

        #region IBlogServer

        /// <summary>
        /// Получение числа подписчиков для произвольного блога
        /// </summary>
        /// <param name="sLink">ссылка на блог</param>
        /// <returns></returns>
        public static uint? HowManyReaders(String sLink)
        {
            String username;
            BlogType bt;
            BlogServer bs = (BlogServer) GetBlogServiceByLink(sLink, out username, out bt);
            uint? iRet = null;
            if (bs != null)
            {
                iRet = bs.HowManyReaders(username, bt);
            }
            //if (iRet == null) //нет реализации у метода извлечения, то обращаемся к Яндексу(!)
            //{
            //    PostWatch pw = new PostWatch();
            //    Match m = pw.ExtractCounts(sLink);
            //    if (m.Success)
            //    {
            //        uint ret;
            //        uint.TryParse(m.Groups[3].Value, out ret);
            //        iRet = ret;
            //    }
            //}
            return iRet;
        }

        /// <summary>
        /// Получение числа комментариев для произвольной записи
        /// </summary>
        /// <param name="sLink">ссылка на блог</param>
        /// <returns></returns>
        public static uint? HowManyComments(String sLink)
        {
            String username;
            BlogType bt;
            BlogServer bs = (BlogServer) GetBlogServiceByLink(sLink, out username, out bt);
            uint? iRet = null;
            if (bs != null)
            {
                iRet = (uint) bs.HowManyComments(BlogPost.SimpleLink(sLink)); //делаем частный вызов
            }
            //if (iRet == null) //нет реализации у метода извлечения, то обращаемся к Яндексу(!)
            //{
            //    PostWatch pw = new PostWatch();
            //    BlogPost bp = BlogPost.SimpleLink(sLink);
            //    if (pw.ExtractLinksComms(bp) && bp.Links >= 0) //пытаемся через яндекс
            //    {
            //        iRet = (uint) bp.Links;
            //    }
            //}
            return iRet;
        }

        /// <summary>
        /// Извлечение html кода Img аватарки для блога.
        /// </summary>
        /// <param name="sLink">ссылка на блог</param>
        /// <returns></returns>
        public static string ExtractAvatar(String sLink)
        {
            String username;
            BlogType bt;
            var bs = GetBlogServiceByLink(sLink, out username, out bt);
            if (bs == null) return String.Empty;
            return bs.ExtractAvatar(username, bt);
        }

        /// <summary>
        /// Получение ссылки на аватарку блогера
        /// </summary>
        /// <param name="server">блогплатформа</param>
        /// <param name="bt">тип блога</param>
        /// <param name="username">имя пользователя</param>
        /// <returns>Ссылку, которая начинается на //a.t30p.ru?</returns>
        public static string GetAvatarLink(BlogServer server, BlogType bt, string username)
        {
            var blognameForAvatar = Common.DelHttp(server.GetBlogLink(username, bt));
            if (server.GetType() == typeof(Rutube))
            {
                blognameForAvatar = "rutube.ru/" + blognameForAvatar.Split('/').Last();
            }
            return server.GetType() == typeof(Mreporter) ? server.ExtractAvatar(username) ://на всякий случай для репортера заглушка на статическую картинку
                                                  ("//a.t30p.ru/?" + HttpUtility.UrlEncode(blognameForAvatar));
        }


        #endregion

        #region Validation and Links

        /// <summary>
        /// Пытаемся определить что за сервис по ссылке, знаем ли мы его
        /// </summary>
        /// <param name="sLink">ссылка на пост или сервис</param>
        /// <param name="shouldHaveInterface">Фильт всех классов по наличию интерфейса, если не NULL</param>
        /// <returns>Если нет подходящего класса, то возвращаем null</returns>
        public static BlogServer GetBlogServiceByLink(String sLink, string shouldHaveInterface = null)
        {
            string uselessname;
            BlogType bt;
            return GetBlogServiceByLink(sLink, out uselessname, out bt, shouldHaveInterface);
        }

        /// <summary>
        /// Пытаемся определить что за сервис по ссылке, знаем ли мы его
        /// </summary>
        /// <param name="sLink">ссылка на пост или сервис</param>
        /// <param name="userName">Имя аккаунта блогера</param>
        /// <param name="bType">Тип блога - сообщество или блог</param>
        /// <param name="shouldHaveInterface">Фильт всех классов по наличию интерфейса, если не NULL</param>
        /// <returns>Если нет подходящего класса, то возвращаем null</returns>
        public static BlogServer GetBlogServiceByLink(String sLink, out String userName, out BlogType bType,
            string shouldHaveInterface = null)
        {
            sLink = FixHttp(sLink); //вообще-то лишняя строчка, но убирать страшно =(((
            foreach (Type blogType in
                EnumerateClassesInNameSpace(typeof (Liveinternet)))
            {
                if (shouldHaveInterface != null && blogType.GetInterface(shouldHaveInterface, true) == null)
                {
                    continue;
                }
                //в 99% у нас получится, что любой блог есть стандалоне, поэтому отложим его на конец
                if (blogType != typeof (StandAlone))
                {
                    var serv = (BlogServer) Activator.CreateInstance(blogType);
                    if ((serv.GetType() == typeof (Instagram) && sLink.Contains("/p/"))
                        || (serv.GetType() == typeof(Periscope) && sLink.Contains("/w/")))
                    {
                        //Важно! Если у нас передана ссылка на пост, то будет сделан нежелательный запрос на сторонний сервис
                        //но мы избегаем этого таким приемом
                        if (serv.ValidateLink(sLink))
                        {
                            userName = null;
                            bType = BlogType.Unknown;
                            return serv;
                        }
                    }
                    else if (serv.ValidateLink(sLink, out userName, out bType))
                    {
                        return serv;
                    }
                }
            }
            var bStandAlone = new StandAlone();
            if (shouldHaveInterface != null && bStandAlone.GetType().GetInterface(shouldHaveInterface, true) == null)
            {
                userName = null;
                bType = BlogType.Unknown;
                return null;
            }
            return bStandAlone.ValidateLink(sLink, out userName, out bType)
                ? bStandAlone.GetOneBlogByName(userName)
                : null;
        }

        /// <summary>
        /// Формируем нормальное имя блога по ссылке на него.
        /// </summary>
        /// <param name="sLink">ссылка на какой-то пост</param>
        /// <param name="storedUserName">Хранимое где-то простой идентификатор пользователя</param>
        /// <returns></returns>
        /// <remarks>Recently renamed from ExtractBlogName</remarks>
        public static string GetBlogNameByLink(String sLink, string storedUserName = null)
        {
            String userName;
            BlogType bType;
            var bs = GetBlogServiceByLink(sLink, out userName, out bType);
            if(bs == null)
            {
                return "";
            }else if (bs.GetType() == typeof(Vimeo))
            {
                userName = storedUserName;//в ссылках на видео нет данных о юзере
            }else if (bs.GetType() == typeof(Rutube))
            {
                return DelHttp((bs as Rutube).GetBlogLink(storedUserName, bType, true));
            }
            return DelHttp(bs.GetBlogLink(userName, bType));
        }

        /// <summary>
        /// Пытаемся определить, что за сервис сокращения ссылок по ссылке, знаем ли мы его
        /// </summary>
        /// <param name="sLink">ссылка на пост или сервис</param>
        /// <returns>Если нет подходящего класса, то возвращаем null</returns>
        public static Shortener GetShortenerByLink(String sLink)
        {
            sLink = FixHttp(sLink); //вообще-то лишняя строчка, но убирать страшно =(((
            foreach (Type blogType in
                EnumerateClassesInNameSpace(typeof (Shorteners.BitLy)))
            {
                var serv = (Shortener) Activator.CreateInstance(blogType);
                if (serv.ValidateLink(sLink))
                {
                    return serv;
                }
            }
            return null;
        }

        /// <summary>
        /// Пытаемся определить, что за сервис обработки фотографий
        /// </summary>
        /// <param name="sLink">ссылка на пост или сервис</param>
        /// <returns>Если нет подходящего класса, то возвращаем null</returns>
        public static ImageServer GetImagerByLink(String sLink)
        {
            if (String.IsNullOrEmpty(sLink)) return null;
            sLink = FixHttp(sLink); //вообще-то лишняя строчка, но убирать страшно =(((
            return EnumerateClassesInNameSpace(typeof (Images.Instagram)).Select(blogType => (ImageServer) Activator.CreateInstance(blogType)).FirstOrDefault(serv => serv.ValidateLink(sLink));
        }

        /// <summary>
        /// Получение списка всех типов серверов
        /// </summary>
        /// <returns></returns>
        public static Type[] EnumerateClassesInNameSpace()
        {
            return EnumerateClassesInNameSpace(typeof (Liveinternet));
        }

        /// <summary>
        /// Get all inherit classes
        /// </summary>
        /// <param name="qClassWithinNamespace"></param>
        /// <returns></returns>
        private static Type[] EnumerateClassesInNameSpace(Type qClassWithinNamespace)
        {
            Type[] tResult = new Type[0];
            Type tType;
            string tNs = qClassWithinNamespace.Namespace;

            Type[] tTypes = qClassWithinNamespace.Assembly.GetTypes();

            foreach (Type t in tTypes)
            {
                tType = t;
                if ((tType.MemberType == MemberTypes.TypeInfo) &&

                    (tType.Namespace == tNs))
                {
                    object[] attributes =
                        tType.GetCustomAttributes(qClassWithinNamespace.GetCustomAttributes(false)[0].GetType(), false);
                    if (attributes != null && attributes.Length > 0)
                    {
                        Array.Resize(ref tResult, tResult.Length + 1);
                        tResult[tResult.Length - 1] = tType;
                    }
                }
            }
            return tResult;
        }

        /// <summary>
        /// Получение объекта типа блога по имени и типу
        /// </summary>
        /// <param name="userName">Имя блога</param>
        /// <param name="blogServ">Имя типа блога</param>
        /// <param name="bType">Коммунити или блог</param>
        /// <returns>Объект типа Uri для дальнейшей работы</returns>
        public static Uri GetBlogUri(String blogServ, String userName, BlogType bType)
        {
            foreach (Type blogserver in EnumerateClassesInNameSpace())
            {
                if (String.Compare(blogserver.Name, blogServ, true) == 0)
                {
                    //создали нужный тип
                    BlogServer serv =
                        (BlogServer) Activator.CreateInstance(blogserver);
                    try
                    {
                        return new Uri(serv.GetBlogLink(userName, bType));
                    }
                    catch (Exception e1)
                    {
                        Debug.WriteLine("Error GetBlogUri: " + e1.Message);
                        return null;
                    }
                }
            }
            return null;
        }

        #endregion

        #region IPosts

        /// <summary>
        /// Публикация одного поста в блогсистеме
        /// </summary>
        /// <param name="whereTo">Ссылка на блог или на пост этого чела, по нему мы определяем что делать-то</param>
        /// <param name="user">Аккаунт с логином и паролем</param>
        /// <param name="bp">Заполненный блог-пост</param>
        /// <returns></returns>
        public static PostResult MakeBlogPost(String whereTo, UserAccount user, BlogPost bp)
        {
            PostResult postResult = new PostResult();
            try
            {
                String user2;
                BlogType bt;
                var server =
                    (BlogServer) GetBlogServiceByLink(FixHttp(whereTo), out user2, out bt);
                //если есть интерфейс постинга, то работаем-с
                if (server.GetType().GetInterface(typeof (IPosts).Name, true) != null)
                {
                    if (String.IsNullOrEmpty(user.username))
                    {
                        user.username = user2;
                    }
                    postResult = server.MakePost(user, bp);
                }
                else
                {
                    postResult.Error = new Exception("Нет интерфейса для постинга");
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error MakeBlogPost: " + e1.Message);
                postResult.Error = e1;
            }
            return postResult;
        }

        /// <summary>
        /// Публикация одного комментария в блогсистеме
        /// </summary>
        /// <param name="whereTo">Ссылка на блог или на пост этого чела, по нему мы определяем что делать-то</param>
        /// <param name="user">Аккаунт с логином и паролем</param>
        /// <param name="cp">Заполненный коммент-пост</param>
        /// <returns></returns>
        public static PostResult MakeCommentPost(String whereTo, UserAccount user, CommentPost cp)
        {
            PostResult postResult = new PostResult();
            try
            {
                String user2;
                BlogType bt;
                BlogServer server =
                    (BlogServer) GetBlogServiceByLink(FixHttp(whereTo), out user2, out bt);
                if (String.IsNullOrEmpty(user.username))
                {
                    user.username = user2;
                }
                //если есть интерфейс комментинга, то работаем-с
                if (server.GetType().GetInterface(typeof (IComments).Name, true) != null)
                {
                    postResult = server.MakeComment(user, cp);
                }
                else if (server.GetType().GetInterface(typeof (IOpenIdLogin).Name, true) != null)
                {
//не можем просто так оставлять комментарии, попробуем OpenID
                    // Note: Bad Code here, need to move it to specific function someday
                    postResult = ((IOpenIdLogin) server).OpenIDComment(
                        new UserOpenID(new Livejournal(), user), cp);
                }
                else
                {
                    postResult.Error = new Exception("Нет интерфейса для комментирования");
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error MakeCommentPost: " + e1.Message);
                postResult.Error = e1;
            }
            return postResult;
        }

        #endregion

        #region IFriends

        /// <summary>
        /// Интерфейс по добавлению френдов
        /// </summary>
        /// <param name="whereTo"></param>
        /// <param name="who">Имя аккаунта для которого меняем френдов</param>
        /// <param name="friends">Набор имен аккаунтов для френдования</param>
        /// <returns>NULL, если все хорошо, иначе ошибку ввиде Exception</returns>
        public static List<String> AddFriends(String whereTo, UserAccount who, params string[] friends)
        {
            try
            {
                String user2;
                BlogType bt;
                var server =
                    (BlogServer) GetBlogServiceByLink(FixHttp(whereTo), out user2, out bt);
                //если есть интерфейс комментинга, то работаем-с
                if (server.GetType().GetInterface(typeof (IFriends).Name, true) != null)
                {
                    if (String.IsNullOrEmpty(who.username))
                    {
                        who.username = user2;
                    }
                    return server.AddFriends(who, friends);
                }
                throw new Exception("Нет интерфейса для редактирования друзей");
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error AddFriends: " + e1.Message);
                return new List<String> {e1.Message};
            }
        }

        /// <summary>
        /// Интерфейс по добавлению френдов
        /// </summary>
        /// <param name="whereTo"></param>
        /// <param name="who">Имя аккаунта для которого меняем френдов</param>
        /// <param name="friends">Набор имен аккаунтов для френдования</param>
        /// <returns>NULL, если все хорошо, иначе ошибку ввиде Exception</returns>
        public static List<String> DelFriends(String whereTo, UserAccount who, params string[] friends)
        {
            try
            {
                String user2;
                BlogType bt;
                var server =
                    (BlogServer) GetBlogServiceByLink(FixHttp(whereTo), out user2, out bt);
                //если есть интерфейс комментинга, то работаем-с
                if (server.GetType().GetInterface(typeof (IFriends).Name, true) != null)
                {
                    if (String.IsNullOrEmpty(who.username))
                    {
                        who.username = user2;
                    }
                    return server.DelFriends(who, friends);
                }
                throw new Exception("Нет интерфейса для редактирования друзей");
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error DelFriends: " + e1.Message);
                return new List<String> {e1.Message};
            }
        }

        /// <summary>
        /// Определяем, зафрендил ли ByFriend наш аккаунт blogLink?
        /// или вступил ли он в наше сообщество.
        /// </summary>
        /// <param name="blogLink">ссылка на блог-сообщество</param>
        /// <param name="friendList">имя френда</param>
        /// <returns>Список тех, кого читает автор блога или является членами сообщества</returns>
        public static List<String> IsFriended(String blogLink, params string[] friendList)
        {
            List<string> oRet = new List<string>(0);
            try
            {
                String user2;
                BlogType bt;
                BlogServer server =
                    (BlogServer) GetBlogServiceByLink(FixHttp(blogLink), out user2, out bt);
                //если есть интерфейс комментинга, то работаем-с
                if (server.GetType().GetInterface(typeof (IFriends).Name, true) != null)
                {
                    oRet = server.IsFriended(user2, bt, friendList);
                }
                else
                {
                    throw new Exception("Нет интерфейса для друзей");
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error IsFriended: " + e1.Message);
            }
            return oRet;
        }

        #endregion

        #region IData Извлечение данных

        /// <summary>
        /// Для некоторых блогплатформ можно унифицировать ссылки
        /// Убрать приставку данных из GA и hashtag
        /// </summary>
        /// <param name="link">ссылка на видео</param>
        /// <returns></returns>
        public static String ToCanonicalLink(string link)
        {
            if (String.IsNullOrWhiteSpace(link)) return String.Empty;
            link = link.Replace("https://", "http://");//убираем https по умолчанию
            return link.Split(new[] { "utm_source=", "#" }, 2, StringSplitOptions.None)[0].TrimEnd('&','?');
        }

        /// <summary>
        /// Извлечение текста поста по ссылке на пост, если он есть в RSS блогохостинга
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        /// <returns>Заполненный объект с содержимым поста</returns>
        public static BlogPost GetPostByRSS(String linkOnPost)
        {
            BlogPost bpRet = null;
            try
            {
                String user;
                BlogType bt;
                var server = GetBlogServiceByLink(FixHttp(linkOnPost), out user, out bt);
                //отдельный случай с Ллео, когда нужно тупо обрабатывать текст...
                bpRet = server.GetOnePost(linkOnPost, user, bt);
                if (bpRet != null && bpRet.Link == null)
                {
                    bpRet.Link = new Uri(linkOnPost);
                }
            }
            catch (Exception e1) //если случилась ошибочка
            {
                Debug.WriteLine("Error GetPostByRSS: " + e1.Message);
            }
            return bpRet;
        }

        /// <summary>
        /// Получение ссылки на RSS
        /// </summary>
        private static readonly Regex rRss =
            new Regex(@"<link(?:[^>]+?type\W+\w+/(?:rss|atom).xml\W|[^>]+?href[^\w>/]+([^>'""]+)){2}\W",
                RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);

        /// <summary>
        /// Извлечение текста поста по ссылке на пост, если он есть в RSS блогохостинга
        /// </summary>
        /// <param name="linkOnPage">ссылка на страницу</param>
        /// <returns>Заполненный объект с содержимым поста</returns>
        public static string GetRSS(String linkOnPage)
        {
            //открываем страничку и ищем тама link="rss"
            string sRssLink = String.Empty;
            try
            {
                Uri linkonPage2;
                if (Uri.TryCreate(linkOnPage, UriKind.Absolute, out linkonPage2))
                {
                    string result = MakeRequest2(linkOnPage).Content.ReadAsStringAsync().Result;
                    //теперь парсим ответ
                    if (String.IsNullOrEmpty(result))
                    {
                        throw new Exception("Получен пустой ответ.");
                    }
                    Match m = rRss.Match(result);
                    if (m.Success)
                    {
                        sRssLink = HttpUtility.HtmlDecode(m.Groups[1].Value.Trim('/'));//окончание ссылки
                        if (!String.IsNullOrEmpty(sRssLink))
                        {
                            if (m.Groups[1].Value[0] == '/' && m.Groups[1].Value[1] != '/')
                            {
                                //относительная ссылка от корня
                                sRssLink = "http://" + linkonPage2.DnsSafeHost + "/" + sRssLink;
                            }
                            else if (!sRssLink.StartsWith("http://") && !sRssLink.StartsWith("https://"))
                            {
                                sRssLink = linkOnPage + "/" + sRssLink;
                            }
                        }
                    }
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Error: " + e1.Message);
            }
            return sRssLink;
        }

        /// <summary>
        /// Добавление кода в текст для отображения спецсимволов с айфонов
        /// </summary>
        /// <param name="text">Текст для проверки</param>
        /// <param name="stringformat">Замена найденного суррогата</param>
        /// <returns>Если stringformat пусто, то просто удаляет суррогаты в тексте</returns>
        public static string DetectSurrogates(string text,
            string stringformat = "<span class=\"emoji emoji{0:x}\"></span>")
        {
            if (String.IsNullOrEmpty(text)) return String.Empty;
            var sbOutput = new StringBuilder();
            char ch;
            int i = 0;
            for (; i < text.Length - 1; i++)
                //! Важно, мы не трогаем последний байт, так как длина суррогатов всеравно 2 байта!
            {
                ch = text[i];
                if (IsUtf8Char(ch))
                {
                    sbOutput.Append(ch);
                }
                else
                {
                    if (!String.IsNullOrEmpty(stringformat))
                    {
                        try
                        {
                            sbOutput.Append(String.Format(stringformat,
                                Char.ConvertToUtf32(ch, text[i + 1])));
                        }
                        catch (ArgumentOutOfRangeException e1)
                        {
                            Debug.WriteLine(text + " : " + e1.Message);
                        }
                    }
                    i++; //пропускаем 1 байт
                }
            }
            if (i == text.Length - 1 && IsUtf8Char(text[i])) //последний символ не был суррогатным
            {
                sbOutput.Append(text[i]);
            }
            return sbOutput.ToString();
        }

        private static bool IsUtf8Char(char ch)
        {
            return (ch >= 0x0020 && ch <= 0xD7FF) ||
                   (ch >= 0xE000 && ch <= 0xFFFD) ||
                   ch == 0x0009 ||
                   ch == 0x000A ||
                   ch == 0x000D;
        }

        #endregion

        #region IVideoHost

        /// <summary>
        /// Определение видеохостинга по ссылке
        /// </summary>
        /// <param name="sLink">ссылка на видео</param>
        /// <returns>объект типа BlogServer</returns>
        public static IVideoHost GetVideoHostByLink(String sLink)
        {
            return GetBlogServiceByLink(sLink, typeof (IVideoHost).Name) as IVideoHost;
        }

        #endregion

    }
}